﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.Xna.Framework;
using TestXNA3;
using TestXNA3.Actors;
using TestXNA3.Render;
using Microsoft.Xna.Framework.Input;
using System.Reflection;
using TestXNA3.Components;
using System.Diagnostics;
using System.IO;

namespace TestEditor
{
    public partial class TestEditor : Form
    {
        public EditorGameStartup _game;

        private bool _mouseLocked = false;
        private List<Type> _typesAddedToBrowser = new List<Type>();
        private SearchActorForm searchActorForm = null;

        public static TestEditor StaticTestEditor = null;

        public TestEditor()
        {
            StaticTestEditor = this;
            InitializeComponent();

            ImportForm.InitProject();

            GameStartup.Width = renderPanel.Width;
            GameStartup.Height = renderPanel.Height;
            _game = new EditorGameStartup();
            this.Show();
            this.TopMost = true;
            this.Disposed += new EventHandler(form_Disposed);
            renderPanel.Resize += new EventHandler(graphics_Resize);
            _game.GraphicsManager.PreparingDeviceSettings += new EventHandler<PreparingDeviceSettingsEventArgs>(graphics_PreparingDeviceSettings);

            Form xnaWindow = (Form)Control.FromHandle((_game.Window.Handle));
            xnaWindow.GotFocus += new EventHandler(xnaWindow_GotFocus);

            renderPanel.Click += new EventHandler(renderPanel_Click);
            renderPanel.MouseDown += new MouseEventHandler(renderPanel_MouseDown);
            renderPanel.MouseUp += new MouseEventHandler(renderPanel_MouseUp);
            renderPanel.MouseMove += new MouseEventHandler(renderPanel_MouseMove);
            this.KeyDown += new KeyEventHandler(TestEditor_KeyDown);

            // Add all available placable actors to actor browser
            Type[] availableTypes = GameStartup.CurrentAssembly.GetExportedTypes();
            int placedTypes = availableTypes.Length;
            int lastPlacedTypeCount = placedTypes - 1;
            while (placedTypes > 0 && lastPlacedTypeCount != placedTypes)
            {
                lastPlacedTypeCount = placedTypes;
                foreach (Type t in availableTypes)
                {
                    bool typeAlreadyAdded = false;
                    foreach (Type addedType in _typesAddedToBrowser)
                    {
                        if (addedType == t)
                        {
                            typeAlreadyAdded = true;
                            break;
                        }
                    }

                    if (typeAlreadyAdded)
                    {
                        break;
                    }

                    if (t == typeof(Actor) && actorBrowser.Nodes.Count < 1)
                    {
                        TreeNode treeNode = new TreeNode(t.FullName.Substring(t.FullName.LastIndexOf('.') + 1));
                        treeNode.Tag = t;
                        actorBrowser.Nodes.Add(treeNode);
                        placedTypes--;
                    }
                    else
                    {
                        foreach (TreeNode treeNode in actorBrowser.Nodes)
                        {
                            if (findParent(t, treeNode))
                            {
                                _typesAddedToBrowser.Add(t);
                                placedTypes--;
                            }
                        }
                    }
                }
            }

            locationSnap.Text = ((EditorGameInfo)_game.GameInfo).LocationSnap.ToString();
            locationSnap.TextChanged += new EventHandler(locationSnap_TextChanged);
            rotationSnap.Text = ((EditorGameInfo)_game.GameInfo).RotationSnap.ToString();
            rotationSnap.TextChanged += new EventHandler(rotationSnap_TextChanged);
            scaleSnap.Text = ((EditorGameInfo)_game.GameInfo).ScaleSnap.ToString();
            scaleSnap.TextChanged += new EventHandler(scaleSnap_TextChanged);
            cameraSpeed.Text = ((EditorGameInfo)_game.GameInfo).cameraSpeed.ToString();
            cameraSpeed.TextChanged += new EventHandler(cameraSpeed_TextChanged);

            actorProperties.MouseClick += new MouseEventHandler(actorProperties_MouseClick);

            _game.Run();
        }

        void cameraSpeed_TextChanged(object sender, EventArgs e)
        {
            float cameraSpd = ((EditorGameInfo)_game.GameInfo).cameraSpeed;
            try
            {
                cameraSpd = float.Parse(cameraSpeed.Text);
            }
            catch
            {
                cameraSpd = ((EditorGameInfo)_game.GameInfo).cameraSpeed;
                scaleSnap.Text = cameraSpd.ToString();
            }
            ((EditorGameInfo)_game.GameInfo).cameraSpeed = cameraSpd;
        }

        void actorProperties_MouseClick(object sender, MouseEventArgs e)
        {
        }

        void actorProperties_MouseDoubleClick(object sender, MouseEventArgs e)
        {
        }

        void scaleSnap_TextChanged(object sender, EventArgs e)
        {
            float scale = ((EditorGameInfo)_game.GameInfo).ScaleSnap;
            try
            {
                scale = float.Parse(scaleSnap.Text);
            }
            catch
            {
                scale = ((EditorGameInfo)_game.GameInfo).ScaleSnap;
                scaleSnap.Text = scale.ToString();
            }
            ((EditorGameInfo)_game.GameInfo).ScaleSnap = scale;
        }

        void rotationSnap_TextChanged(object sender, EventArgs e)
        {
            float rotation = ((EditorGameInfo)_game.GameInfo).RotationSnap;
            try
            {
                rotation = float.Parse(rotationSnap.Text);
            }
            catch
            {
                rotation = ((EditorGameInfo)_game.GameInfo).RotationSnap;
                rotationSnap.Text = rotation.ToString();
            }
            ((EditorGameInfo)_game.GameInfo).RotationSnap = rotation;
        }

        void locationSnap_TextChanged(object sender, EventArgs e)
        {
            float location = ((EditorGameInfo)_game.GameInfo).LocationSnap;
            try
            {
                location = float.Parse(locationSnap.Text);
            }
            catch
            {
                location = ((EditorGameInfo)_game.GameInfo).LocationSnap;
                locationSnap.Text = location.ToString();
            }
            ((EditorGameInfo)_game.GameInfo).LocationSnap = location;
        }

        void renderPanel_MouseMove(object sender, MouseEventArgs e)
        {
            EditorGameInfo gameInfo = (EditorGameInfo)_game.GameInfo;

            if (gameInfo.SelectedActor != null && gameInfo.SelectedAxis != EditorGameInfo.ESelectedAxis.None)
            {
                Plane p = new Plane();
                Vector3 axis = Vector3.Zero;
                if (gameInfo.SpaceType == EditorGameInfo.ESpaceType.Local)
                {
                    switch (gameInfo.SelectedAxis)
                    {
                        case EditorGameInfo.ESelectedAxis.AxisX:
                            axis = Vector3.Right;
                            p = new Plane(gameInfo.SelectedActor.Location + Vector3.Right
                                , gameInfo.SelectedActor.Location + Vector3.Up, gameInfo.SelectedActor.Location);
                            break;

                        case EditorGameInfo.ESelectedAxis.AxisY:
                            axis = Vector3.Up;
                            p = new Plane(gameInfo.SelectedActor.Location + Vector3.Up
                                , gameInfo.SelectedActor.Location + Vector3.Forward, gameInfo.SelectedActor.Location);
                            break;

                        case EditorGameInfo.ESelectedAxis.AxisZ:
                            axis = -Vector3.Forward;
                            p = new Plane(gameInfo.SelectedActor.Location + Vector3.Forward
                                , gameInfo.SelectedActor.Location + Vector3.Up, gameInfo.SelectedActor.Location);
                            break;
                    };
                }

                axis.Normalize();

                Vector3 startTrace = _game.GameInfo.MainCamera.Unproject(e.Location.X
                    , e.Location.Y, 0.0f);
                Vector3 endTrace = _game.GameInfo.MainCamera.Unproject(e.Location.X
                    , e.Location.Y, 1.0f);

                Vector3 traceDir = endTrace - startTrace;
                traceDir.Normalize();
                Ray ray = new Ray(startTrace, traceDir);

                float? t = ray.Intersects(p);
                if (t.HasValue)
                {
                    Vector3 intersectionPoint = startTrace + traceDir * t.Value;

                    Vector3 toPoint = intersectionPoint - gameInfo.SelectedActor.Location;
                    toPoint.Normalize();
                    float dist = (intersectionPoint - gameInfo.SelectedActor.Location).Length();

                    if (dist > 1f)
                    {
                        Vector3 dir = axis * toPoint;

                        if (gameInfo.TransformType == EditorGameInfo.ETransformType.Translation)
                        {
                            Vector3 snappedLocation = gameInfo.SelectedActor.Location + dir * dist;
                            gameInfo.SelectedActor.Location = gameInfo.SnapLocation(snappedLocation);
                        }else
                        if (gameInfo.TransformType == EditorGameInfo.ETransformType.Rotation)
                        {
                            //float snappedAngle = 0.025f * dist;
                            switch (gameInfo.SelectedAxis)
                            {
                                case EditorGameInfo.ESelectedAxis.AxisX:
                                    //gameInfo.SelectedActor.Yaw = toPoint.X * t.Value * 0.1f;
                                    break;

                                case EditorGameInfo.ESelectedAxis.AxisY:
                                    //gameInfo.SelectedActor.Pitch = toPoint.Y * dist;
                                    break;

                                case EditorGameInfo.ESelectedAxis.AxisZ:
                                    //gameInfo.SelectedActor.Roll = toPoint.Z * dist;
                                    break;
                            };
                        }else
                        if (gameInfo.TransformType == EditorGameInfo.ETransformType.Scaling)
                        {
                        }
                    }
                }
            }
        }

        void TestEditor_KeyDown(object sender, KeyEventArgs e)
        {
        }

        public void keyDown(System.Windows.Forms.Keys key)
        {
            if (key == System.Windows.Forms.Keys.G)
            {
                ((EditorGameInfo)_game.GameInfo).DrawGrid = !((EditorGameInfo)_game.GameInfo).DrawGrid;
            }
        }

        private void addToNode(Type t, TreeNode treeNode)
        {
            TreeNode newTreeNode = new TreeNode(t.FullName.Substring(t.FullName.LastIndexOf('.') + 1));
            newTreeNode.Tag = t;
            treeNode.Nodes.Add(newTreeNode);
        }

        private bool findParent(Type t, TreeNode baseTreeNode)
        {
            string parentClassName = t.BaseType.FullName.Substring(t.BaseType.FullName.LastIndexOf('.') + 1);
            if (baseTreeNode.Text == parentClassName)
            {
                addToNode(t, baseTreeNode);
                return true;
            }
            foreach (TreeNode treeNode in baseTreeNode.Nodes)
            {
                if (treeNode.Text == parentClassName)
                {
                    addToNode(t, treeNode);
                    return true;
                }
                bool found = findParent(t, treeNode);
                if (found) return true;
            }
            return false;
        }

        void renderPanel_Click(object sender, EventArgs e)
        {
            EditorGameInfo gameInfo = (EditorGameInfo)_game.GameInfo;
            if (gameInfo.SelectedActor != null && gameInfo.TransformType == EditorGameInfo.ETransformType.Rotation)
            {
                gameInfo.skipRotationForFrames = 2;
            }
            //_mouseLocked = !_mouseLocked;
            //((EditorGameInfo)_game.GameInfo).MouseLocked = _mouseLocked;
        }

        public void notifyRightMousePressed()
        {
            //MouseState mouseState = Mouse.GetState();
            //float winX = ((EditorGameInfo)_game.GameInfo).SelectedAxisStartDrag.X;
            //float winY = ((EditorGameInfo)_game.GameInfo).SelectedAxisStartDrag.Y;
            //winX = renderPanel.PointToClient(new System.Drawing.Point((int)winX, (int)winY)).X;
            //winY = renderPanel.PointToClient(new System.Drawing.Point((int)winX, (int)winY)).Y;
            
            Vector3 start = _game.GameInfo.MainCamera.Unproject(((EditorGameInfo)_game.GameInfo).SelectedAxisStartDrag.X
                , ((EditorGameInfo)_game.GameInfo).SelectedAxisStartDrag.Y, 0.0f);
            Vector3 end = _game.GameInfo.MainCamera.Unproject(((EditorGameInfo)_game.GameInfo).SelectedAxisStartDrag.X
                , ((EditorGameInfo)_game.GameInfo).SelectedAxisStartDrag.Y, 1.0f);

            Vector3 dir = (end - start);
            dir.Normalize();

            Ray ray = new Ray(start, dir);
            Plane p = new Plane(new Vector3(0, 1, 0), 0f);
            float? intersectionDistance = ray.Intersects(p);

            float lastDist = 999999f;
            _game.GameInfo.Scene.traceSelfAndChilds(_game.GameInfo.Scene, start, end, true, ref lastDist);
            if (lastDist < 999999f)
            {
                intersectionDistance = new System.Nullable<float>(lastDist);
            }
            
            if (intersectionDistance.HasValue && selectedActorType.Tag != null)
            {
                Vector3 intersectionPoint = ray.Position + ray.Direction * intersectionDistance.Value;

                if (((EditorGameInfo)_game.GameInfo).SelectedActor != null)
                {
                    ((EditorGameInfo)_game.GameInfo).SelectedActor.DrawBoundingBox = false;
                }
                Actor a = (Actor)Activator.CreateInstance((Type)selectedActorType.Tag);
                _game.GameInfo.Scene.AttachActor(a);
                a.Location = ((EditorGameInfo)_game.GameInfo).SnapLocation(intersectionPoint);
                SelectActor(a);
            }
        }

        public void SelectActor(Actor a)
        {
            EditorGameInfo gameInfo = ((EditorGameInfo)_game.GameInfo);
            if (gameInfo.SelectedActor != null)
            {
                gameInfo.SelectedActor.DrawBoundingBox = false;
                actorProperties.SelectedObject = null;
                gameInfo.SelectedActor = null;
            }
            if (a != null)
            {
                a.DrawBoundingBox = true;
                gameInfo.SelectedActor = a;
                actorProperties.SelectedObject = a;
            }
        }

        void renderPanel_MouseDown(object sender, MouseEventArgs e)
        {
            renderPanel.Focus();

            float winX = (float)e.Location.X;
            float winY = (float)e.Location.Y;
            Vector3 start = _game.GameInfo.MainCamera.Unproject(winX, winY, 0.0f);
            Vector3 end = _game.GameInfo.MainCamera.Unproject(winX, winY, 1.0f);
            Vector3 dir = (end - start);
            dir.Normalize();

            EditorGameInfo gameInfo = (EditorGameInfo)_game.GameInfo;

            if (gameInfo.SelectedActor != null)
            {
                float distanceToActor = gameInfo.calcAxisExtension();
                BoundingBox triBounds = new BoundingBox(gameInfo.SelectedActor.Location - new Vector3(distanceToActor * 0.5f),
                    gameInfo.SelectedActor.Location + new Vector3(distanceToActor * 0.5f));

                Vector3 rayStart = gameInfo.MainCamera.Unproject(e.Location.X, e.Location.Y, 0f);
                Vector3 rayEnd = gameInfo.MainCamera.Unproject(e.Location.X, e.Location.Y, 1f);
                Vector3 rayDir = rayEnd - rayStart;
                rayDir.Normalize();
                Ray mousePickRay = new Ray(rayStart, rayDir);

                if (triBounds.Intersects(mousePickRay).HasValue)
                {
                    BoundingBox rightBox = gameInfo.SelectedActorRightBox;
                    BoundingBox upBox = gameInfo.SelectedActorUpBox;
                    BoundingBox forwardBox = gameInfo.SelectedActorForwardBox;

                    if (upBox.Intersects(mousePickRay).HasValue)
                    {
                        gameInfo.SelectedAxis = EditorGameInfo.ESelectedAxis.AxisY;
                    }
                    else
                    if (rightBox.Intersects(mousePickRay).HasValue)
                    {
                        gameInfo.SelectedAxis = EditorGameInfo.ESelectedAxis.AxisX;
                    }
                    else
                    if (forwardBox.Intersects(mousePickRay).HasValue)
                    {
                        gameInfo.SelectedAxis = EditorGameInfo.ESelectedAxis.AxisZ;
                    }
                    else
                    {
                        gameInfo.SelectedAxis = EditorGameInfo.ESelectedAxis.None;
                    }
                }
            }

            if (e.Button == MouseButtons.Right)
            {
                if (gameInfo.SelectedActor != null && gameInfo.SelectedAxis != EditorGameInfo.ESelectedAxis.None && gameInfo.TransformType == EditorGameInfo.ETransformType.Translation)
                {
                    object newObject = Activator.CreateInstance(gameInfo.SelectedActor._Type);
                    Actor duplicatedActor = (Actor)newObject;
                    _game.GameInfo.Scene.AttachActor(duplicatedActor);
                    gameInfo.SelectedActor.DuplicateTo(duplicatedActor);
                    SelectActor(duplicatedActor);
                }
                else
                {
                    ((EditorGameInfo)_game.GameInfo).RightMousePressed = true;
                    ((EditorGameInfo)_game.GameInfo).SelectedAxisStartDrag = new Vector3(e.Location.X, e.Location.Y, 0f);
                }
            }

            if (e.Button == MouseButtons.Left)
            {
                if (gameInfo.SelectedActor == null || gameInfo.SelectedAxis == EditorGameInfo.ESelectedAxis.None)
                {
                    //Actor a = _game.GameInfo.Scene.TraceScene(start, end, true);
                    float lastDist = 999999f;
                    Actor a = _game.GameInfo.Scene.traceSelfAndChilds(_game.GameInfo.Scene, start, end, true, ref lastDist);
                    SelectActor(a);
                }
            }
        }

        void renderPanel_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                ((EditorGameInfo)_game.GameInfo).RightMousePressed = false;
            }
            if (e.Button == MouseButtons.Left)
            {
                ((EditorGameInfo)_game.GameInfo).SelectedAxis = EditorGameInfo.ESelectedAxis.None;
            }
        }

        void xnaWindow_GotFocus(object sender, EventArgs e)
        {
            ((Form)sender).Visible = false;
            this.TopMost = false;
        }

        void graphics_PreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e)
        {
            e.GraphicsDeviceInformation.PresentationParameters.DeviceWindowHandle = renderPanel.Handle;
        }

        void graphics_Resize(object sender, EventArgs e)
        {
            GameStartup.Width = renderPanel.Width;
            GameStartup.Height = renderPanel.Height;

            _game.GraphicsManager.PreferredBackBufferWidth = renderPanel.Width;
            _game.GraphicsManager.PreferredBackBufferHeight = renderPanel.Height;
            _game.GraphicsManager.ApplyChanges();
        }

        
        void form_Disposed(object sender, EventArgs e)
        {
            _game.Exit();
        }

        private void renderPanel_Paint(object sender, PaintEventArgs e)
        {
        }

        private void toolStripMoveActor_Click(object sender, EventArgs e)
        {
            ((EditorGameInfo)_game.GameInfo).TransformType = EditorGameInfo.ETransformType.Translation;
        }

        private void toolStripButton2_Click(object sender, EventArgs e)
        {
            ((EditorGameInfo)_game.GameInfo).TransformType = EditorGameInfo.ETransformType.Rotation;
        }

        private void toolStripSaveMap_Click(object sender, EventArgs e)
        {
            saveFileDialog1.InitialDirectory = Directory.GetCurrentDirectory() + "\\Content\\Maps\\";
            saveFileDialog1.Filter = "Test3 map|*.t3";
            saveFileDialog1.ShowDialog();
            if (saveFileDialog1.FileName.Length > 1)
            {
                _game.GameInfo.Map.Save(saveFileDialog1.FileName.Substring(saveFileDialog1.FileName.LastIndexOf('\\')));
            }
        }

        private void toolStripOpenMap_Click(object sender, EventArgs e)
        {
            openFileDialog1.InitialDirectory = Directory.GetCurrentDirectory() + "\\Content\\Maps\\";
            openFileDialog1.Filter = "Test3 map|*.t3";
            openFileDialog1.ShowDialog();
            if (openFileDialog1.FileName.Length > 1)
            {
                _game.GameInfo = new EditorGameInfo();
                _game.GameInfo.Initialize();
                _game.GameInfo.Map.Load(openFileDialog1.FileName.Substring(openFileDialog1.FileName.LastIndexOf('\\')));
            }
        }

        private void actorBrowser_AfterSelect(object sender, TreeViewEventArgs e)
        {
            selectedActorType.Text = e.Node.Text;
            selectedActorType.Tag = e.Node.Tag;
        }

        private void toolStripNewMap_Click(object sender, EventArgs e)
        {
            _game.GameInfo = new EditorGameInfo();
            _game.GameInfo.Initialize();
        }

        private void selectedPropertyProperties_Click(object sender, EventArgs e)
        {
            if (actorProperties.SelectedGridItem != null)
            {
                object o = actorProperties.SelectedGridItem.Value;
                if (o != null)
                {
                    if (o is StaticMeshComponent)
                    {
                        StaticMeshComponentProperties propDlg = new StaticMeshComponentProperties();
                        propDlg.StaticMeshComponent = o as StaticMeshComponent;
                        propDlg.Init();
                        propDlg.Show();
                    }
                }
            }
        }

        private void searchActorButton_Click(object sender, EventArgs e)
        {
            if (searchActorForm != null)
            {
                searchActorForm.Hide();
            }
            searchActorForm = new SearchActorForm();
            searchActorForm.updateFoundTimer_Tick(null, null);
            searchActorForm.Show();
        }

        private void actorPropsTimes_Tick(object sender, EventArgs e)
        {
            if (!actorProperties.RectangleToScreen(actorProperties.ClientRectangle).Contains(Cursor.Position.X, Cursor.Position.Y))
            {
                actorProperties.Refresh();
            }
            /*
            object selectedObject = actorProperties.SelectedObject;
            object[] selectedObjects = actorProperties.SelectedObjects;
            actorProperties.SelectedObject = null;
            actorProperties.SelectedObjects = null;
            if (selectedObject != null)
            {
                actorProperties.SelectedObject = selectedObject;
            }
            else
            {
                actorProperties.SelectedObjects = selectedObjects;
            }*/
        }

        private void importToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ImportForm importForm = new ImportForm(ImportForm.EImportType.StaticMesh);
            importForm.ShowDialog();
        }

        private void importTextureToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ImportForm importForm = new ImportForm(ImportForm.EImportType.Texture);
            importForm.ShowDialog();
        }

        private void importSkeletalMeshToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ImportForm importForm = new ImportForm(ImportForm.EImportType.SkeletalMesh);
            importForm.ShowDialog();
        }

        private void animTreeEditorToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Process proc = Process.Start("XMasEditor.exe");
        }

        private void newToolStripMenuItem_Click(object sender, EventArgs e)
        {
            toolStripNewMap_Click(null, null);
        }

        private void loadToolStripMenuItem_Click(object sender, EventArgs e)
        {
            toolStripOpenMap_Click(null, null);
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            toolStripSaveMap_Click(null, null);
        }
    }
}
